package com.shelpe.services.algorithm.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.shelpe.services.algorithm.entity.jpa.Semiauto;
import com.shelpe.services.algorithm.entity.jpa.Semiauto.Type;
import com.shelpe.services.algorithm.repository.dto.semiauto.SemiautoCondition;
import com.shelpe.services.algorithm.repository.dto.semiauto.SemiautoMatchScopeModel;
import com.shelpe.services.algorithm.repository.dto.semiauto.SemiautoSettings;
import com.shelpe.services.algorithm.repository.jpa.SemiautoRepository;
import com.shelpe.services.algorithm.semiauto.SemiautoConditionsInitializer;
import com.shelpe.services.algorithm.semiauto.SemiautoFilter;
import com.shelpe.services.algorithm.utils.SemiautoUtils;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Controller
@RequestMapping("/algorithmservice")
@Slf4j
public class SemiautoController {

	@Autowired
	private SemiautoRepository semiautoRepository;
	@Autowired
	private List<SemiautoFilter> filters;
	@Autowired
	private List<SemiautoConditionsInitializer> conditionsInitializers;

	@RequestMapping(path = "/semiautos/{semiauto_id}", method = RequestMethod.GET)
	@ResponseBody
	public SemiautoDto findSemiauto(@PathVariable("semiauto_id") long semiautoID) {
		Semiauto po = this.semiautoRepository.findOne(semiautoID);
		if (po == null) {
			throw new IllegalArgumentException("semiauto_not_found");
		}

		return this.toDto(po);
	}
	
	@RequestMapping(path = "/semiautos/{semiauto_id}", method = RequestMethod.PUT)
	@ResponseBody
	public SemiautoDto updateSemiauto(@PathVariable("semiauto_id") long semiautoID, @RequestBody SemiautoDto dto) {
		Semiauto po = this.semiautoRepository.findOne(semiautoID);
		if (po == null) {
			throw new IllegalArgumentException("semiauto_not_found");
		}
		if(dto.getSettings() != null){
			po.setContent(SemiautoUtils.serializeSettings(dto.getSettings()));
		}
		if(dto.getDevice() > -1){
			if(dto.getDevice() == SemiautoDto.DEVICE_MOBILE){
				po.setDevice(Semiauto.DEVICE_MOBILE);
			}else{
				po.setDevice(Semiauto.DEVICE_PC);
			}
		}
		if(dto.getTitle() != null){
			po.setTitle(dto.getTitle());
		}
		if(dto.getType() != null){
			po.setType(Type.to(dto.getType()));
		}
		this.convertSemiautoType(dto, po.getUserID(), po);
		this.semiautoRepository.save(po);
		return this.toDto(po);
	}
	
	@RequestMapping(path = "/users/{user_id}/semiautos", method = RequestMethod.POST)
	@ResponseBody
	public SemiautoDto createSemiauto(@PathVariable("user_id") long userID, @RequestBody SemiautoDto dto) {
		Semiauto po = new Semiauto();
		BeanUtils.copyProperties(dto, po);
		po.setCreateTime(new Date());
		po.setUserID(userID);
		if(dto.getSettings() != null){
			po.setContent(SemiautoUtils.serializeSettings(dto.getSettings()));
		}
		po.setStatus(Semiauto.Status.normal);
		if(dto.getType() != null){
			po.setType(Type.to(dto.getType()));
		}
		if(dto.getPlatform() != null){
			po.setPlatform(Semiauto.Platform.valueOf(dto.getPlatform()));
		}else{
			po.setPlatform(Semiauto.Platform.special);
		}
		if(dto.getDevice() == SemiautoDto.DEVICE_MOBILE){
			po.setDevice(Semiauto.DEVICE_MOBILE);
		}else{
			po.setDevice(Semiauto.DEVICE_PC);
		}
		po.setAdgroupID("");
		po.setCampaignID("");
		this.convertSemiautoType(dto, userID, po);
		this.semiautoRepository.save(po);
		return this.toDto(po);
	}
	
	private String joinAsComma(List<Long> ids){
		StringBuffer buffer = new StringBuffer();
		for(long id: ids){
			buffer.append(id).append(",");
		}
		return buffer.substring(0, buffer.length()-1);
	}
	
	private void convertSemiautoType(SemiautoDto dto, long userID, Semiauto po){
		if(po.getType() == Semiauto.Type.batch){
			po.setTypeValue(userID + "");
		}
		if(po.getType() == Semiauto.Type.product){
			if(dto.getCampaignIDs() != null && dto.getCampaignIDs().size() > 0){
				po.setCampaignID(this.joinAsComma(dto.getCampaignIDs()));
			}
			if(dto.getAdgroupIDs() != null && dto.getAdgroupIDs().size() > 0){
				po.setAdgroupID(this.joinAsComma(dto.getAdgroupIDs()));
			}
		}
	}
	
	private SemiautoDto toDto(Semiauto po){
		SemiautoDto dto = new SemiautoDto();
		BeanUtils.copyProperties(po, dto);
		dto.fromDevice(po.getDevice());
		if (po.getStatus() != null) {
			dto.setStatus(po.getStatus().name());
		}
		if (po.getPlatform() != null) {
			dto.setPlatform(po.getPlatform().name());
		}
		if(po.getType() != null){
			dto.setType(po.getType().name());
		}

		String content = po.getContent();
		dto.setSettings(SemiautoUtils.unserializeSettings(content));
		return dto;
	}

	@Data
	@JsonInclude(Include.NON_NULL)
	public static class SemiautoDto {
		public final static int DEVICE_ALL = 0;
		public final static int DEVICE_MOBILE = 1;
		public final static int DEVICE_PC = 2;

		@JsonProperty("semiauto_id")
		private long autoID;
		private String title;
		private SemiautoSettings settings;
		private String status;
		private String platform;

		private String type;
		@JsonProperty("adgroup_ids")
		private List<Long> adgroupIDs;
		@JsonProperty("campaign_ids")
		private List<Long> campaignIDs;
		@JsonProperty("user_id")
		private long userID;
		
		private int device;

		public void fromDevice(int device) {
			if (device == Semiauto.DEVICE_MOBILE) {
				this.device = SemiautoDto.DEVICE_MOBILE;
			} else {
				this.device = SemiautoDto.DEVICE_PC;
			}
		}
	}

	@RequestMapping(path = "/semiautos/{semiauto_id}/keywords", method = RequestMethod.POST)
	@ResponseBody
	public KeywordsDto applyKeywords(@PathVariable("semiauto_id") long semiautoID, @RequestBody KeywordsDto dto) {

		Semiauto po = this.semiautoRepository.findOne(semiautoID);
		if (po == null) {
			throw new IllegalArgumentException("semiauto_not_found");
		}

		SemiautoSettings settings = SemiautoUtils.unserializeSettings(po.getContent());
		if (settings == null) {
			throw new IllegalArgumentException("semiauto_setting_not_found");
		}
		log.debug("settings: {}", settings);

		List<Keyword> words = new ArrayList<Keyword>(dto.getWords().size() * 2);
		words.addAll(dto.getWords());

		SemiautoConditionsInitializer.Context context = new SemiautoConditionsInitializer.Context();
		context.setAdgroupID(dto.getAdgroupID());
		context.setInterval(settings.getDate());
		context.setUserID(po.getUserID());
		if(po.getDevice() == 0){
			context.setMobile(true);
		}
		if (settings.getConditions() != null) {
			for (SemiautoCondition condition : settings.getConditions()) {
				for (SemiautoConditionsInitializer initializer : this.conditionsInitializers) {
					initializer.run(condition, context);
				}
			}
		}
		for (Keyword word : words) {
			boolean supported = this.support(word, settings.getConditions());

			word.setPrice(word.getMaxPrice());
			word.setNewMatchScope(word.getMatchScope());

			if (settings.getDeleteModel() != null && settings.getDeleteModel().getStatus() > 0) {
				if (supported) {
					word.addAction("remove");
				}
			} else {
				if (settings.getMatchScopeModel() != null && settings.getMatchScopeModel().getStatus() > 0 && supported) {
					word.addAction("match");
					int ms = word.getMatchScope();
					if (settings.getMatchScopeModel().getValue() == SemiautoMatchScopeModel.MATCH_SCOPE_WIDE) {
						ms = 4;
					}
					if (settings.getMatchScopeModel().getValue() == SemiautoMatchScopeModel.MATCH_SCOPE_CENTRAL) {
						ms = 2;
					}
					if (settings.getMatchScopeModel().getValue() == SemiautoMatchScopeModel.MATCH_SCOPE_PRECISION) {
						ms = 1;
					}
					word.setNewMatchScope(ms);
					word.setModifiedStatus(1);
				}
				if (settings.getPriceModel() != null && settings.getPriceModel().getStatus() > 0 && supported) {
					word.addAction("price");
					word.setPrice(settings.getPriceModel().pricing(word.getMaxPrice()));
					word.setMobilePrice(settings.getPriceModel().pricing(word.getMaxMobilePrice()));
					word.setModifiedStatus(1);
				}
			}

			word.setMatched(supported);
		}

		return dto;
	}

	private boolean support(Keyword word, List<SemiautoCondition> conditions) {
		boolean supported = true;
		for (SemiautoCondition condition : conditions) {
			boolean status = true;
			for (SemiautoFilter filter : this.filters) {
				if (filter.support(condition)) {
					status = filter.filter(word, condition);
					break;
				}
			}
			if (SemiautoCondition.METHOD_AND.equals(condition.getMethod())) {
				supported = supported && status;
				if (!supported) {
					break;
				}
			}

			if (SemiautoCondition.METHOD_OR.equals(condition.getMethod())) {
				supported = supported || status;
				if (supported) {
					break;
				}
			}
		}

		return supported;
	}

	@Data
	public static class KeywordsDto {
		@JsonProperty("adgroup_id")
		private long adgroupID;
		private List<Keyword> words;
	}

	@Data
	public static class Keyword {
		@JsonProperty("keyword_id")
		private long keywordID;
		private String word;
		@JsonProperty("max_price")
		private float maxPrice;
		@JsonProperty("max_mobile_price")
		private float maxMobilePrice;
		@JsonProperty("impressions")
		private int impression;// 展现量
		@JsonProperty("matchscope")
		private int matchScope;
		private int click;// 点击量
		@JsonProperty("ctr")
		private float clickRatio;// 点击率
		private float cpc;// 平均点击花费
		private float cpm;
		private float cvr;// 点击转化率
		@JsonProperty("avgpos")
		private int productRanking;// 宝贝排名
		private int qscore;// 质量得分
		@JsonProperty("wireless_qscore")
		private int mobileQscore;// 移动质量得分
		private float cost;// 总花费
		@JsonProperty("sales")
		private float sale;// 总金额
		@JsonProperty("total_pay_count")
		private int payCount;// 成交笔数
		private float roi;// 投入产出比
		@JsonProperty("fav_count")
		private int favCount;// 收藏数
		@JsonProperty("fav_count_rate")
		private float favCountRatio;// 收藏率
		@JsonProperty("cart_count")
		private int cartCount;// 购物车数
		@JsonProperty("cart_count_rate")
		private float cartCountRatio;// 购物车率
		@JsonProperty("is_default_price")
		private boolean asDefaultPrice;
		@JsonProperty("is_default_mobile_price")
		private boolean asDefaultMobilePrice;

		private String action;
		@JsonProperty("new_matchscope")
		private int newMatchScope;
		@JsonProperty("new_price")
		private float price;
		@JsonProperty("new_mobile_price")
		private float mobilePrice;
		private int modifiedStatus;
		private boolean matched;

		public void addAction(String action) {
			if (this.action == null) {
				this.action = action;
			} else {
				this.action = this.action + "," + action;
			}
		}
	}

}
